#include "SWM320.h"
#include "delay.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#include "py/compile.h"
#include "py/runtime.h"
#include "py/repl.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "lib/utils/pyexec.h"
#include "lib/mp-readline/readline.h"
#include "lib/utils/interrupt_char.h"

#include "lib/oofatfs/ff.h"
#include "lib/oofatfs/diskio.h"
#include "extmod/vfs.h"
#include "extmod/vfs_fat.h"

#define UART_BUF_LENGTH_MAX 269
#define MPY_HEAP_SIZE 80 * 1024
extern int mp_hal_stdin_rx_chr(void);

static char *stack_top;
#if MICROPY_ENABLE_GC
static char heap[MPY_HEAP_SIZE];
#endif
static const char fresh_main_py[] = "# main.py -- put your code here!\r\n";
static const char fresh_boot_py[] = "# boot.py -- run on boot-up\r\n"
                                    "# can run arbitrary Python, but best to keep it minimal\r\n"
                                    ;

STATIC void init_sflash_filesystem (void);
void do_str(const char *src, mp_parse_input_kind_t input_kind);
const uint8_t Banner[] = {" __          __  _    _  __     __   _____              _   _ \r\n\
 \\ \\        / / | |  | | \\ \\   / /  / ____|     /\\     | \\ | |\r\n\
  \\ \\  /\\  / /  | |__| |  \\ \\_/ /  | |         /  \\    |  \\| |\r\n\
   \\ \\/  \\/ /   |  __  |   \\   /   | |        / /\\ \\   | . ` |\r\n\
    \\  /\\  /    | |  | |    | |    | |____   / ____ \\  | |\\  |\r\n\
     \\/  \\/     |_|  |_|    |_|     \\_____| /_/    \\_\\ |_| \\_|\r\n\
Official Site:http://www.whycan.cn/\r\n\
SiteEmail:admin@whycan.cn\r\n\
AuthEmail:1766769299@qq.com,2535418266@qq.com"};
#define UART_RX_LEN	 128
char UART_RXBuffer[UART_RX_LEN] = {0};
uint32_t UART_RXIndex = 0;
void SerialInit(void);
void UART0_Handler(void)
{
	uint32_t chr;
	
	if(UART_INTRXThresholdStat(UART0) || UART_INTTimeoutStat(UART0))
	{
		while(UART_IsRXFIFOEmpty(UART0) == 0)
		{
			if(UART_ReadByte(UART0, &chr) == 0)
			{
				if(UART_RXIndex < UART_RX_LEN)
				{
					// printf("interrupt cur chr %c\r\n",chr);
					UART_RXBuffer[UART_RXIndex] = chr;
					if (chr == mp_interrupt_char) {
						mp_keyboard_interrupt();
					}
					UART_RXIndex++;
				}
				// pyexec_event_repl_process_char(chr);

			}
		}
	}
}

uint32_t UART_GetChars(char *data)
{
	uint32_t len = 0;
	
	if(UART_RXIndex != 0)
	{
		NVIC_DisableIRQ(UART0_IRQn);		//��UART_RXBuffer��ȡ���ݹ�����Ҫ�ر��жϣ���ֹ��д����
		memcpy(data, UART_RXBuffer, UART_RX_LEN);
		len = UART_RXIndex;
		UART_RXIndex = 0;
		NVIC_EnableIRQ(UART0_IRQn);
	}
	
	return len;
}

void Tasktest(void *arg)
{	
	GPIO_Init(GPIOA, PIN5, 1, 0, 0);
	while(1)
	{
		// printf("Tasktest rinning\r\n");
		// _write(0, "Tasktest\r\n", strlen("Tasktest\r\n")+1);
		GPIO_InvBit(GPIOA, PIN5);
		vTaskDelay(1000);
	}
}
void Taskmpy(void *arg)
{	
	// while(1){
	// 	_write(0, "Taskmpy\r\n", strlen("Taskmpy\r\n")+1);
	// 	vTaskDelay(1000);
	// }
	_write(0, "Taskmpy\r\n", strlen("Taskmpy\r\n")+1);
	uint32_t i;
	int stack_dummy;  
	stack_top = (char*)&stack_dummy;
	#if MICROPY_ENABLE_GC
	gc_init(heap, heap + sizeof(heap));
	#endif
	mp_init();
	readline_init0();


    mp_obj_list_init(mp_sys_path, 0);
    mp_obj_list_init(mp_sys_argv, 0);
    mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)

    // initialize the serial flash file system
    init_sflash_filesystem();

    // append the flash paths to the system path
    mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_slash_flash));
    mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_slash_flash_slash_lib));

    // reset config variables; they should be set by boot.py
    MP_STATE_PORT(machine_config_main) = MP_OBJ_NULL;

    // run boot.py
    // int ret = pyexec_file("boot.py");
    // if (ret & PYEXEC_FORCED_EXIT) {
    //     // goto err_forever;
    // }
    // if (!ret) {
    //     //mperror_signal_error();
    // }

    // // run main.py
    // ret = pyexec_file("main.py");
    // if (ret & PYEXEC_FORCED_EXIT) {
    //     // goto err_forever;
    // }
    // if (!ret) {
    //     //mperror_signal_error();
    // }

	_write(0, "Taskmpy\r\n", strlen("Taskmpy\r\n")+1);
	// readline_process_char(27);
	// pyexec_frozen_module("boot.py");
	#if MICROPY_REPL_EVENT_DRIVEN
		uint32_t err;
		int chr;
		pyexec_event_repl_init();
		mp_hal_set_interrupt_char(CHAR_CTRL_C);
		char c = 0;
		uint32_t len;
		char buffer[UART_RX_LEN] = {0};
		NVIC_EnableIRQ(UART0_IRQn);
		for (;;) {
			if((len = UART_GetChars(buffer)) != 0)
			{
				for(i = 0; i < len; i++)
				{
					// printf("cur chr:%c\r\n",buffer[i]);
					// if (chr == mp_interrupt_char) {
					// 	mp_keyboard_interrupt();
					// }
					uint32_t ret = pyexec_event_repl_process_char(buffer[i]);
					if (ret & PYEXEC_FORCED_EXIT) {
						break;
					}
					// printf("%c called\r\n",buffer[i]);
				}
			}
			
		// while(UART_IsRXFIFOEmpty(UART0));
		
		// err = UART_ReadByte(UART0, &chr);
		// if(err == 0)
		// {
		// 	if(pyexec_event_repl_process_char(chr)) {
		// 		break;
		// 	}
		// }
		// else if(err == UART_ERR_PARITY)
		// {
		// 	printf("Parity error!\r\n");
			
		// 	// while(1) __NOP();
		// }
			__NOP();
		}
	#else
		pyexec_friendly_repl();
	#endif
	mp_deinit();
}
int main(void)
{
 	
 	
 	SystemInit();
	switchCLK_PLL();//切换到100MHZ
	SystemCoreClockUpdate();
	// switchCLK_40MHz();
	SerialInit();
	printf("sysclk:%fmhz\r\n",SystemCoreClock/1000.00/1000.00);
	// delay_init();
	RTC_Start(RTC);
	printf(Banner);
	Taskmpy(0);
	// _write(0, "xTaskCreate\r\n", strlen("xTaskCreate\r\n")+1);
	// // printf("xTaskCreate\r\n");
	// BaseType_t tmp_r=xTaskCreate(Tasktest, (const char *)"a", 128, NULL, 3, NULL);
	// if(tmp_r == pdPASS){
	// 	_write(0, "Tasktest start\r\n", strlen("Tasktest start\r\n")+1);
	// }
	// tmp_r=xTaskCreate(Taskmpy, (const char *)"mpy", 1024, NULL, 1, NULL);
	// if(tmp_r == pdPASS){
	// 	_write(0, "Taskmpy start\r\n", strlen("Taskmpy start\r\n")+1);
	// }
	// _write(0, "vTaskStartScheduler\r\n", strlen("vTaskStartScheduler\r\n")+1);
	// vTaskStartScheduler();
	
	_write(0, "reboot\r\n", strlen("reboot\r\n")+1);
	NVIC_SystemReset();
}
int _write(int fd, char *ptr, int len)
{
	int i;
	for(i = 0; i < len; i++)
	{
		UART_WriteByte(UART0, *ptr++);
	
		while(UART_IsTXBusy(UART0));
	}
 	
	return len;
}
void SerialInit(void)
{
	UART_InitStructure UART_initStruct;
	
	PORT_Init(PORTA, PIN2, FUNMUX0_UART0_RXD, 1);	//GPIOA.2����ΪUART0��������
	PORT_Init(PORTA, PIN3, FUNMUX1_UART0_TXD, 0);	//GPIOA.3����ΪUART0�������
 	
 	UART_initStruct.Baudrate = 115200;
	UART_initStruct.DataBits = UART_DATA_8BIT;
	UART_initStruct.Parity = UART_PARITY_NONE;
	UART_initStruct.StopBits = UART_STOP_1BIT;
	UART_initStruct.RXThreshold = 1;
	UART_initStruct.RXThresholdIEn = 1;
	UART_initStruct.TXThreshold = 3;
	UART_initStruct.TXThresholdIEn = 0;
	UART_initStruct.TimeoutTime = 10;
	UART_initStruct.TimeoutIEn = 1;
 	UART_Init(UART0, &UART_initStruct);
	NVIC_DisableIRQ(UART0_IRQn);
	UART_Open(UART0);
}


/****************************************************************************************************************************************** 
* ��������: fputc()
* ����˵��: printf()ʹ�ô˺������ʵ�ʵĴ��ڴ�ӡ����
* ��    ��: int ch		Ҫ��ӡ���ַ�
*			FILE *f		�ļ����
* ��    ��: ��
* ע������: ��
******************************************************************************************************************************************/
int fputc(int ch, FILE *f)
{
	UART_WriteByte(UART0, ch);
	
	while(UART_IsTXBusy(UART0));
 	
	return ch;
}

// int fputc(int ch, FILE *f)
// {
//  	while(UART_IsTXFIFOFull(UART0));
	
// 	UART_WriteByte(UART0, ch);
 	
// 	return ch;
// }

void do_str(const char *src, mp_parse_input_kind_t input_kind) {
    nlr_buf_t nlr;
    if (nlr_push(&nlr) == 0) {
        mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
        qstr source_name = lex->source_name;
        mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
        mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
        mp_call_function_0(module_fun);
        nlr_pop();
    } else {
        // uncaught exception
        mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
    }
}

void nlr_jump_fail(void *val) {
    while (1);
}
#if MICROPY_ENABLE_GC
void gc_collect(void) {
    // WARNING: This gc_collect implementation doesn't try to get root
    // pointers from CPU registers, and thus may function incorrectly.
    void *dummy;
    gc_collect_start();
    gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t));
    gc_collect_end();
    gc_dump_info();
}
#endif

#if !MICROPY_DEBUG_PRINTERS
// With MICROPY_DEBUG_PRINTERS disabled DEBUG_printf is not defined but it
// is still needed by esp-open-lwip for debugging output, so define it here.
#include <stdarg.h>
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args);
int DEBUG_printf(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int ret = mp_vprintf(MICROPY_DEBUG_PRINTER, fmt, ap);
    va_end(ap);
    return ret;
}
#endif
// mp_import_stat_t mp_import_stat(const char *path) {
// 	    return MP_IMPORT_STAT_NO_EXIST;
// }

// mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
// 	    return mp_const_none;
// }
// MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open);
// mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
// 	    mp_raise_OSError(MP_ENOENT);
// }
STATIC int vfs_mount_and_chdir(mp_obj_t bdev, mp_obj_t mount_point) {
    nlr_buf_t nlr;
    mp_int_t ret = -MP_EIO;
    if (nlr_push(&nlr) == 0) {
        mp_obj_t args[] = { bdev, mount_point };
        mp_vfs_mount(2, args, (mp_map_t*)&mp_const_empty_map);
        mp_vfs_chdir(mount_point);
        ret = 0; // success
        nlr_pop();
    } else {
        mp_obj_base_t *exc = nlr.ret_val;
        if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_OSError))) {
            mp_obj_t v = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(exc));
            mp_obj_get_int_maybe(v, &ret); // get errno value
            ret = -ret;
        }
    }
    return ret;
}
fs_user_mount_t vfs_fat;
STATIC void init_sflash_filesystem (void) {
    FILINFO fno;

    // Initialise the local flash filesystem.
    // init the vfs object
    
    vfs_fat.blockdev.flags = MP_BLOCKDEV_FLAG_FREE_OBJ;
    swm320_flash_init_vfs(&vfs_fat);

    // Create it if needed, and mount in on /flash.
    FRESULT res;
    FRESULT mnt_res = f_mount(&(vfs_fat.fatfs));
    if (mnt_res == FR_NO_FILESYSTEM) {
		printf("no filesystem, so create a fresh one %d\r\n",mnt_res);
        // no filesystem, so create a fresh one
        uint8_t working_buf[FF_MAX_SS];
        res = f_mkfs(&(vfs_fat.fatfs), FM_FAT | FM_SFD, 0, working_buf, sizeof(working_buf));
        if (res != FR_OK) {
            printf("failed to create /flash %d\r\n",res);
            while(1) __NOP();
        }
    }

    // mount the flash device (there should be no other devices mounted at this point)
    // we allocate this structure on the heap because vfs->next is a root pointer
    mp_vfs_mount_t *vfs = m_new_obj_maybe(mp_vfs_mount_t);
    if (vfs == NULL) {
        goto fail;
    }
    vfs->str = "/flash";
    vfs->len = 6;
    vfs->obj = MP_OBJ_FROM_PTR(&vfs_fat);
    vfs->next = NULL;
    MP_STATE_VM(vfs_mount_table) = vfs;

    MP_STATE_PORT(vfs_cur) = vfs;

	// Default block device to entire flash storage
	// extern const mp_obj_base_t swm320_flash_obj;
    // mp_obj_t bdev = MP_OBJ_FROM_PTR(&swm320_flash_obj);
	// // Try to mount the flash on "/flash" and chdir to it for the boot-up directory.
    // mp_obj_t mount_point = MP_OBJ_NEW_QSTR(MP_QSTR_flash);
    // int ret = vfs_mount_and_chdir(bdev, mount_point);

    // // if (ret == -MP_ENODEV && bdev == MP_OBJ_FROM_PTR(&swm320_flash_obj)) {
    //     // No filesystem, bdev is still the default (so didn't detect a possibly corrupt littlefs),
    //     // and didn't already create a filesystem, so try to create a fresh one now.
    //     // ret = factory_reset_create_filesystem();
    //     // if (ret == 0) {
    //         ret = vfs_mount_and_chdir(bdev, mount_point);
    //     // }
    // // }

    // if (ret != 0) {
    //     printf("MPY: can't mount flash\n");
    //     return false;
    // }

    // create /flash/sys, /flash/lib if they don't exist
    if (mnt_res == FR_NO_FILESYSTEM) {
        if (FR_OK != f_chdir(&vfs_fat.fatfs, "/sys")) {
            f_mkdir(&vfs_fat.fatfs, "/sys");
        }
        if (FR_OK != f_chdir(&vfs_fat.fatfs, "/lib")) {
            f_mkdir(&vfs_fat.fatfs, "/lib");
        }
    }
    f_chdir(&vfs_fat.fatfs, "/");

    // make sure we have a /flash/boot.py.  Create it if needed.
    if (mnt_res == FR_NO_FILESYSTEM) {
        res = f_stat(&vfs_fat.fatfs, "/boot.py", &fno);
        if (res != FR_OK) {
            // doesn't exist, create fresh file
            FIL fp;
            f_open(&vfs_fat.fatfs, &fp, "/boot.py", FA_WRITE | FA_CREATE_ALWAYS);
            UINT n;
            f_write(&fp, fresh_boot_py, sizeof(fresh_boot_py) - 1 /* don't count null terminator */, &n);
            f_close(&fp);
        }

        res = f_stat(&vfs_fat.fatfs, "/main.py", &fno);
        if (res != FR_OK) {
            // doesn't exist, create fresh file
            FIL fp;
            f_open(&vfs_fat.fatfs, &fp, "/main.py", FA_WRITE | FA_CREATE_ALWAYS);
            UINT n;
            f_write(&fp, fresh_main_py, sizeof(fresh_main_py) - 1 /* don't count null terminator */, &n);
            f_close(&fp);
        }
    }
    return;

fail:
    //printf("failed to create /flash");
    while(1) __NOP();
}

DWORD get_fattime(void) {
    //timeutils_struct_time_t tm;
    //timeutils_seconds_since_2000_to_struct_time(pyb_rtc_get_seconds(), &tm);

    // return ((tm.tm_year - 1980) << 25) | ((tm.tm_mon) << 21)  |
    //         ((tm.tm_mday) << 16)       | ((tm.tm_hour) << 11) |
    //         ((tm.tm_min) << 5)         | (tm.tm_sec >> 1);

    return 0;
}